/*
 * Copyright 2000-2006 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.intellij.gwt.run;

import com.intellij.gwt.module.GwtModulesManager;
import com.intellij.gwt.module.model.GwtModule;
import com.intellij.gwt.GwtBundle;
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
import com.intellij.openapi.fileTypes.FileTypeManager;
import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.options.SettingsEditor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.xml.XmlFile;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.ui.ComboboxWithBrowseButton;
import com.intellij.ui.RawCommandLineEditor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import javax.swing.event.ChangeListener;
import javax.swing.event.ChangeEvent;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;

public class GwtRunConfigurationEditor extends SettingsEditor<GwtRunConfiguration> {
  private DefaultComboBoxModel myModulesModel;
  private DefaultComboBoxModel myPagesModel;
  private JComboBox myModulesBox;
  private JPanel myMainPanel;
  private ComboboxWithBrowseButton myHtmlPageBox;
  private JLabel myHtmlToOpenLabel;
  private RawCommandLineEditor myVMParameters;
  private Project myProject;
  private GwtModulesManager myGwtModulesManager;
  private JCheckBox myNoServer;
  private JTextField myServerUrlTextField;
  private JLabel myServerUrlLabel;

  public GwtRunConfigurationEditor(Project project) {
    myProject = project;
    myGwtModulesManager = GwtModulesManager.getInstance(myProject);
  }

  public void resetEditorFrom(GwtRunConfiguration configuration) {
    myVMParameters.setDialodCaption(GwtBundle.message("dialog.caption.vm.parameters"));
    myVMParameters.setText(configuration.VM_PARAMETERS);

    myModulesModel.removeAllElements();
    for (Module module : configuration.getValidModules()) {
      myModulesModel.addElement(module);
    }
    myModulesModel.setSelectedItem(configuration.getModule());

    fillPages(configuration.getModule());
    final VirtualFile file = page2HtmlFile(configuration);
    if (file != null) {
      myHtmlPageBox.getComboBox().getEditor().setItem(file.getPresentableUrl());
    }
    myNoServer.setSelected(configuration.NO_SERVER);
    if (StringUtil.isNotEmpty(configuration.SERVER_URL)) {
      myServerUrlTextField.setText(configuration.SERVER_URL);
    }
  }

  private @Nullable VirtualFile page2HtmlFile(final GwtRunConfiguration configuration) {
    String page = configuration.RUN_PAGE;
    final int index = page.indexOf('/');
    if (index == -1) return null;

    final Module module = configuration.getModule();
    if (module == null) {
      return null;
    }

    GwtModule gwtModule = myGwtModulesManager.findGwtModuleByName(page.substring(0, index), GlobalSearchScope.moduleWithDependenciesScope(module));
    if (gwtModule == null) return null;

    String name = page.substring(index + 1);
    final List<VirtualFile> publicRoots = gwtModule.getPublicRoots();
    for (VirtualFile root : publicRoots) {
      final VirtualFile file = root.findFileByRelativePath(name);
      if (file != null) {
        return file;
      }
    }
    return null;
  }

  private @Nullable String htmlFile2PageName(VirtualFile file) {
    if (file == null) {
      return "";
    }
    final GwtModule gwtModule = myGwtModulesManager.findGwtModuleByClientOrPublicFile(file);
    final String path = myGwtModulesManager.getPathFromPublicRoot(file);
    if (gwtModule != null && path != null) {
      return gwtModule.getQualifiedName() + "/" + path;
    }
    else {
      return null;
    }
  }

  private void fillPages(final Module module) {
    myPagesModel.removeAllElements();
    if (module == null) return;

    final GwtModule[] modules = myGwtModulesManager.getGwtModules(module);
    for (GwtModule gwtModule : modules) {
      final XmlFile htmlFile = myGwtModulesManager.findHtmlFileByModule(gwtModule);
      if (htmlFile != null) {
        final VirtualFile file = htmlFile.getVirtualFile();
        if (file != null) {
          myPagesModel.addElement(file.getPath());
        }
      }
    }
  }

  public void applyEditorTo(GwtRunConfiguration configuration) throws ConfigurationException {
    configuration.setModule((Module)myModulesBox.getSelectedItem());
    final String path = (String)myHtmlPageBox.getComboBox().getEditor().getItem();
    VirtualFile file = LocalFileSystem.getInstance().findFileByPath(FileUtil.toSystemIndependentName(path));
    configuration.RUN_PAGE = htmlFile2PageName(file);
    configuration.VM_PARAMETERS = myVMParameters.getText();
    configuration.NO_SERVER = myNoServer.isSelected();
    configuration.SERVER_URL = myServerUrlTextField.getText();
  }

  @NotNull
  public JComponent createEditor() {
    myModulesModel = new DefaultComboBoxModel();
    myModulesBox.setModel(myModulesModel);
    myPagesModel = new DefaultComboBoxModel();
    final JComboBox comboBox = myHtmlPageBox.getComboBox();
    comboBox.setEditable(true);
    comboBox.setModel(myPagesModel);
    myHtmlToOpenLabel.setLabelFor(comboBox);

    myModulesBox.setRenderer(new DefaultListCellRenderer() {
      public Component getListCellRendererComponent(JList list, final Object value, int index, boolean isSelected, boolean cellHasFocus) {
        super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
        final Module module = (Module)value;
        if (module != null) {
          setIcon(module.getModuleType().getNodeIcon(false));
          setText(module.getName());
        }
        return this;
      }
    });

    myModulesBox.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        fillPages((Module)myModulesModel.getSelectedItem());
      }
    });

    myNoServer.addChangeListener(new ChangeListener() {
        public void stateChanged(ChangeEvent event) {
            JCheckBox component = (JCheckBox) event.getSource();
            myServerUrlLabel.setEnabled(component.isSelected());
            myServerUrlTextField.setEnabled(component.isSelected());
        }
    }
    );

    myHtmlPageBox.addBrowseFolderListener(myProject, createHtmlFileChooserDescriptor());

    return myMainPanel;
  }

  private FileChooserDescriptor createHtmlFileChooserDescriptor() {
    final FileChooserDescriptor descriptor = new FileChooserDescriptor(true, false, false, false, false, false) {
      public boolean isFileVisible(VirtualFile file, boolean showHiddenFiles) {
        return super.isFileVisible(file, showHiddenFiles) &&
               (file.isDirectory() || FileTypeManager.getInstance().getFileTypeByFile(file) == StdFileTypes.HTML);
      }
    };
    final VirtualFile[] sourceRoots = ProjectRootManager.getInstance(myProject).getContentSourceRoots();
    descriptor.getRoots().clear();
    for (VirtualFile sourceRoot : sourceRoots) {
      descriptor.addRoot(sourceRoot);
    }
    return descriptor;
  }

  public void disposeEditor() {
  }
}
